package controller

import (
	"encoding/json"
	"fileNest/config"
	"fileNest/dao"
	"fileNest/global"
	"fileNest/model"
	"fileNest/service"
	"fileNest/storage"
	"fileNest/util"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
)

// 根据文件夹id查询文件夹内容
func GetFolderById(ctx *gin.Context) {
	tokenString := ctx.GetHeader("Authorization")
	_, userID, err := util.ParseJWT(tokenString)
	if err != nil {
		util.FailResponse(ctx, fmt.Errorf("token验证失败"), 401)
		return
	}

	folderId := ctx.Query("folderId")
	isDel := ctx.Query("isDel")
	query := bson.M{}
	var result model.FileEntryResultList = []model.FileEntryResult{}

	var parentFolder = model.FileEntry{
		IsDir:  true,
		UserID: userID,
		PathList: []model.PathItem{
			model.PathItem{
				Name: "我的文件夹",
			},
		},
	}
	if isDel == "true" {
		var query = bson.M{
			"user_id": userID,
			"is_del":  true,
		}
		if err := dao.DBFindMoreWithOutPage("entries", query, &result); err != nil {
			util.FailResponse(ctx, err)
			return
		}
	} else {
		if folderId == "" {
			// 没有id => 顶层
			query["parent_id"] = nil
		} else {
			objectID, err := primitive.ObjectIDFromHex(folderId)
			if err != nil {
				util.FailResponse(ctx, err)
				return
			}
			query["parent_id"] = objectID
			query["user_id"] = userID
			parentFolderBson := bson.M{
				"_id": objectID,
			}
			if err := dao.DBFindOne("entries", parentFolderBson, &parentFolder); err != nil {
				util.FailResponse(ctx, err)
				return
			}

			// 将本身的路径追加到父级路径中
			parentFolder.PathList = append(parentFolder.PathList, model.PathItem{
				ID:   parentFolder.ID,
				Name: parentFolder.Name,
			})
		}
		query["is_del"] = false
		if err := dao.DBFindMoreWithOutPage("entries", query, &result); err != nil {
			util.FailResponse(ctx, err)
			return
		}
	}
	res := model.FileEntryResult{
		FileEntry: parentFolder,
		Children:  result,
	}
	util.SuccessResponse(ctx, res)

}

// 获取目录树
func GetFolderTree(ctx *gin.Context) {
	token := ctx.GetHeader("Authorization")
	_, userId, err := util.ParseJWT(token)
	if err != nil {
		util.FailResponse(ctx, err)
	}
	var result = model.FileEntryList{}

	// 匹配所有user_id的文件夹
	query := bson.M{
		"user_id": userId,
		"is_dir":  true,
		"is_del":  false,
	}
	if err = dao.DBFindMoreWithOutPage("entries", query, &result); err != nil {
		util.FailResponse(ctx, err)
		return
	}
	// 转换类型
	resultSlice := result.FormatToFileEntryResult()
	// 组装树形结构
	resultSlice.BuildFolderTree()
	util.SuccessResponse(ctx, resultSlice)
}

// 新增文件夹
func AddFolder(ctx *gin.Context) {
	tokenString := ctx.GetHeader("Authorization")
	if tokenString == "" {
		util.FailResponse(ctx, fmt.Errorf("token验证失败"), 401)
		return
	}
	_, userID, err := util.ParseJWT(tokenString)
	if err != nil {
		util.FailResponse(ctx, fmt.Errorf("token验证失败"), 401)
		return
	}

	var data model.FileEntry
	if err := ctx.ShouldBind(&data); err != nil {
		util.FailResponse(ctx, err)
		return
	}
	var value = bson.M{
		"name":      data.Name,
		"parent_id": data.ParentID,
		"is_dir":    data.IsDir,
		"user_id":   userID,
	}
	var result model.FileEntry
	if err := dao.DBFindOne("entries", value, &result); err == nil {
		util.FailResponse(ctx, fmt.Errorf("文件夹名称重复"))
		return
	} else if err != mongo.ErrNoDocuments {
		util.FailResponse(ctx, err)
		return
	}

	var pathList []model.PathItem
	if data.ParentID != nil {
		// 子文件夹
		var parent model.FileEntry
		var query = bson.M{
			"_id": data.ParentID,
		}
		if err := dao.DBFindOne("entries", query, &parent); err != nil {
			util.FailResponse(ctx, fmt.Errorf("父级文件夹不存在"))
			return
		}

		pathList = append(parent.PathList, model.PathItem{
			ID:   parent.ID,
			Name: parent.Name,
		})
	} else {
		// 顶级文件夹（没有 parent）
		pathList = []model.PathItem{
			model.PathItem{Name: "我的文件夹"},
		}
	}
	value["path_list"] = pathList
	value["storage_path"] = nil
	value["size"] = nil
	value["mime_type"] = nil
	value["is_del"] = false
	value["created_at"] = time.Now()
	value["updated_at"] = nil
	_, err = dao.DBAdd("entries", value)
	if err != nil {
		util.FailResponse(ctx, err)
		return
	}
	util.SuccessResponse(ctx, nil, "创建成功")
}

// 生成预览url
func GetPreviewUrl(ctx *gin.Context) {
	url := ctx.Query("url")
	url, err := config.GeneratePresignedURL("file-nest-storage", url)
	if err != nil {
		util.FailResponse(ctx, err)
		return
	}
	util.SuccessResponse(ctx, url)
}

// 更新文件&文件夹信息
func UpdateFile(ctx *gin.Context) {
	var data model.FileEntry
	if err := ctx.ShouldBind(&data); err != nil {
		util.FailResponse(ctx, err)
		return
	}
	var filter = bson.M{
		"_id": data.ID,
	}
	var update = bson.M{
		"$set": bson.M{
			"name": data.Name,
		},
	}
	result, err := dao.UpdateOne("entries", filter, update)
	if err != nil {
		util.FailResponse(ctx, err)
		return
	}
	if data.IsDir {
		err := service.UpdatePathListNameByID(*data.ID, data.Name)
		if err != nil {
			util.FailResponse(ctx, err)
			return
		}
	}
	util.SuccessResponse(ctx, result)
}

// 根据id删除文件&文件夹
func DelFile(ctx *gin.Context) {
	id := ctx.Query("id")
	isDir := ctx.Query("isDir")
	if id == "" {
		util.FailResponse(ctx, fmt.Errorf("id不能为空"))
		return
	}
	objectID, err := primitive.ObjectIDFromHex(id)
	if err != nil {
		util.FailResponse(ctx, err)
		return
	}
	if isDir == "false" {
		var filter = bson.M{
			"_id": objectID,
		}
		var update = bson.M{
			"$set": bson.M{
				"is_del":   true,
				"deleteAt": time.Now(),
			},
		}
		if _, err := dao.UpdateOne("entries", filter, update); err != nil {
			util.FailResponse(ctx, err)
			return
		}
	} else {
		var ids []primitive.ObjectID
		// 将自身id放进去
		ids = append(ids, objectID)
		var result model.FileEntryList
		if err := dao.DBFindMoreWithOutPage("entries", bson.M{"_id": objectID}, &result); err != nil {
			util.FailResponse(ctx, err)
			return
		}
		if err := result.GetFolderRecursively(&ids); err != nil {
			util.FailResponse(ctx, err)
			return
		}
		var filter = bson.M{
			"_id": bson.M{
				"$in": ids,
			},
		}
		var update = bson.M{
			"$set": bson.M{
				"is_del":   true,
				"deleteAt": time.Now(),
			},
		}

		if _, err := dao.UpdateMany("entries", filter, update); err != nil {
			util.FailResponse(ctx, err)
			return
		}
	}

	util.SuccessResponse(ctx, nil, "删除成功")
}

// 回收站还原文件
func Restore(ctx *gin.Context) {
	id := ctx.Query("id")
	isDir := ctx.Query("isDir")
	objectID, err := primitive.ObjectIDFromHex(id)
	if err != nil {
		util.FailResponse(ctx, err)
		return
	}
	if isDir == "false" {
		var filter = bson.M{
			"_id": objectID,
		}
		var update = bson.M{
			"$set": bson.M{
				"is_del":   false,
				"deleteAt": nil,
			},
		}
		_, err = dao.UpdateOne("entries", filter, update)
		if err != nil {
			util.FailResponse(ctx, err)
			return
		}
	} else {
		var ids []primitive.ObjectID
		ids = append(ids, objectID)
		var result model.FileEntryList
		if err := result.GetFolderRecursively(&ids); err != nil {
			util.FailResponse(ctx, err)
			return
		}
		var filter = bson.M{
			"_id": bson.M{
				"$in": ids,
			},
		}
		var update = bson.M{
			"$set": bson.M{
				"is_del":   false,
				"deleteAt": nil,
			},
		}

		if _, err := dao.UpdateMany("entries", filter, update); err != nil {
			util.FailResponse(ctx, err)
			return
		}
	}
	service.RestoreWithParents(objectID)
	util.SuccessResponse(ctx, nil, "恢复成功")
}

// 多个文件打包为压缩包下载
func DownloadFile(ctx *gin.Context) {
	body, err := io.ReadAll(ctx.Request.Body)
	if err != nil {
		util.FailResponse(ctx, err, http.StatusBadRequest)
		return
	}
	var idsStr []string
	if err := json.Unmarshal(body, &idsStr); err != nil {
		util.FailResponse(ctx, err, http.StatusBadRequest)
		return
	}

	var ids = []primitive.ObjectID{}

	for _, v := range idsStr {
		objectID, _ := primitive.ObjectIDFromHex(v)

		ids = append(ids, objectID)

	}
	var result = model.FileEntryList{}
	var filter = bson.M{
		"_id": bson.M{
			"$in": ids,
		},
	}

	err = dao.DBFindMoreWithOutPage("entries", filter, &result)
	if err != nil {
		util.FailResponse(ctx, err, http.StatusInternalServerError)
		return
	}

	var files = []storage.DownloadObjectType{}
	for _, v := range result {
		obj, err := storage.DowmloadMinioObject(global.BucketName, v.StoragePath, v.Name)
		if err != nil {
			util.FailResponse(ctx, err, http.StatusInternalServerError)
			break
		}
		files = append(files, obj)
	}
	util.DownloadZipHandler(ctx, "压缩包", files)
}
